
#include "StdAfx.h"
#include "Soldier.h"
#include "Building.h"
#include "Level.h"
#include "Camera.h"
#include "OBJReader.h"
#include "PhysX.h"
#include "CookingStream.h"
#include "InputMesh.h"
#include "ObjMesh.h"
#include "BuildingIndexBuffer.h"
#include "SpotLight.h"
#include "Skeleton.h"
#include "ShadowCamera.h"
#include "SpikeySphere.h"
#include "CubePosColorNorm.h"


Soldier::Soldier(const tstring &texfilename, float radius, int slices, int stacks, ContentManager* Manager, int life, int range, int damage)
	:m_TextureName(texfilename), m_Radius(radius), m_NumSlices(slices), m_NumStacks(stacks), m_Opacity(1)
	,m_pVertexLayout(0)
	,m_pVertexBuffer(0)
	,m_pIndexBuffer(0)
	,m_pDefaultEffect(0)	
	,m_pDefaultTechnique(0)	
	,m_pWorldViewProjectionVariable(NULL)
	,m_pDiffuseMapVariabele(0)
	,m_pTextureRV(0)
	,m_bStatic(true)	
	,m_Time(0)
	,m_Pickup(false)
	,m_DeleteOnlyOnce(false)
	,m_GotPath(false)
	,m_GotDestination(false)
	,m_IsAttacking(false)
	,m_EnemyTargetBuilding(0)
	,m_EnemyTargetSoldier(0)
	,m_AttackTime(0)
	,m_Life(life)
	, m_Range(range)
	, m_Damage(damage)
	,m_Manager(Manager)
{
}

Soldier::~Soldier(void)
{
	SafeRelease(m_pVertexBuffer );
	SafeRelease(m_pIndexBuffer );
	SafeRelease(m_pVertexLayout);
}
void Soldier::Initialize(ContentManager *pContentManager)
{
	CreateEffect(pContentManager);
	m_pTextureRV = pContentManager->GetTexture(m_pLevel->GetDevice(), m_TextureName);
	GetEffectVariables(m_pDefaultEffect);	
	DefineInputlayout();
	BuildShape();
	BuildVertexBuffer();
	BuildIndexBuffer();
	InitActor();
}
void Soldier::CreateEffect(ContentManager *pContentManager)
{
	m_pDefaultEffect = pContentManager->GetEffect(m_pLevel->GetDevice(),  _T("./Effect/SpikeyShader.fx"));
	
	//get first technique found
	m_pDefaultTechnique = m_pDefaultEffect->GetTechniqueByIndex(0);
	if(!m_pDefaultTechnique->IsValid())
	{
		MessageBox(0,_T("Technique not valid"),_T("ERROR"),0);
		exit(-1);
	}
}
void Soldier::GetEffectVariables(ID3D10Effect* pEffect)
{
	//get effect variables
	m_pWorldViewProjectionVariable = pEffect->GetVariableBySemantic("WorldViewProjection")->AsMatrix();
	if(!m_pWorldViewProjectionVariable->IsValid())
	{
		MessageBox(0,_T("Getting EffectSemantic WorldViewProjection Failed"),_T("ERROR"),0);
		exit(-1);
	}

	m_pWorldVariable = pEffect->GetVariableBySemantic("World")->AsMatrix();
	if(!m_pWorldVariable->IsValid())
	{
		MessageBox(0,_T("Getting EffectSemantic World Failed"),_T("ERROR"),0);
		exit(-1);
	}

	m_pLightDirVariabele = pEffect->GetVariableByName("m_LightDir")->AsVector();
	if(!m_pLightDirVariabele->IsValid())
	{
		MessageBox(0,_T("Getting EffectSemantic LightDir Failed"),_T("ERROR"),0);
		exit(-1);
	}

	m_pDiffuseMapVariabele = pEffect->GetVariableByName( "m_TextureDiffuse" )->AsShaderResource();
	if(!m_pDiffuseMapVariabele->IsValid())
	{
		MessageBox(0,_T("Getting EffectSemantic DiffuseMap Failed"),_T("ERROR"),0);
		exit(-1);
	}

	m_OpacityVar = pEffect->GetVariableByName( "Opacity" )->AsScalar();
	if(!m_OpacityVar->IsValid())
	{
		MessageBox(0,_T("Getting EffectSemantic Opacity Failed"),_T("ERROR"),0);
		exit(-1);
	}
}
void Soldier::DefineInputlayout()
{
    // Define the input layout
    D3D10_INPUT_ELEMENT_DESC layout[] =
    {
        { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },  
        { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 },  
        { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D10_INPUT_PER_VERTEX_DATA, 0 }, 
    };
    UINT numElements = sizeof(layout)/sizeof(layout[0]);

    // Create the input layout
    D3D10_PASS_DESC PassDesc;
	// Get the pass decriptor from the effect technique
    m_pDefaultTechnique->GetPassByIndex( 0 )->GetDesc( &PassDesc );
    HR(m_pLevel->GetDevice()->CreateInputLayout( layout, numElements, PassDesc.pIAInputSignature, PassDesc.IAInputSignatureSize, &m_pVertexLayout ));
}

void Soldier::BuildShape()
{
	float phiStep = (float)D3DX_PI/m_NumStacks;

	// do not count the poles as rings
	UINT numRings = m_NumStacks-1;

	// Compute vertices for each stack ring.
	for(UINT i = 1; i <= numRings; ++i)
	{
		float phi = i*phiStep;

		// vertices of ring
		float thetaStep = 2.0f*(float)D3DX_PI/m_NumSlices;
		for(int j = 0; j <= m_NumSlices; ++j)
		{
			float theta = j*thetaStep;

			VertexPosNormTex v;

			// spherical to cartesian
			v.pos.x = m_Radius*sinf(phi)*cosf(theta);
			v.pos.y = m_Radius*cosf(phi);
			v.pos.z = m_Radius*sinf(phi)*sinf(theta);

			//// partial derivative of P with respect to theta
			//v.tangent.x = -m_Radius*sinf(phi)*sinf(theta);
			//v.tangent.y = 0.0f;
			//v.tangent.z = m_Radius*sinf(phi)*cosf(theta);

			D3DXVec3Normalize(&v.normal, &v.pos);
			
			v.tex.x = theta / (2.0f*(float)D3DX_PI);
			v.tex.y = phi / (float)D3DX_PI;

			m_VecVertices.push_back( v );
		}
	}

	// poles: note that there will be texture coordinate distortion
	m_VecVertices.push_back( VertexPosNormTex(D3DXVECTOR3(0.0f, -m_Radius, 0.0f), D3DXVECTOR3(0.0f,-1.0f, 0.0f),D3DXVECTOR2(0,0)));
	m_VecVertices.push_back( VertexPosNormTex(D3DXVECTOR3(0.0f,  m_Radius, 0.0f), D3DXVECTOR3(0.0f, 1.0f, 0.0f),D3DXVECTOR2(0,1)));

	int northPoleIndex = (UINT)m_VecVertices.size()-1;
	int southPoleIndex = (UINT)m_VecVertices.size()-2;

	int numRingVertices = m_NumSlices+1;

	// Compute indices for inner stacks (not connected to poles).
	for(int i = 0; i < m_NumStacks-2; ++i)
	{
		for(int j = 0; j < m_NumSlices; ++j)
		{
			m_VecIndices.push_back(i*numRingVertices + j);
			m_VecIndices.push_back(i*numRingVertices + j+1);
			m_VecIndices.push_back((i+1)*numRingVertices + j);

			m_VecIndices.push_back((i+1)*numRingVertices + j);
			m_VecIndices.push_back(i*numRingVertices + j+1);
			m_VecIndices.push_back((i+1)*numRingVertices + j+1);
		}
	}

	// Compute indices for top stack.  The top stack was written 
	// first to the vertex buffer.
	for(int i = 0; i < m_NumSlices; ++i)
	{
		m_VecIndices.push_back(northPoleIndex);
		m_VecIndices.push_back(i+1);
		m_VecIndices.push_back(i);
	}

	// Compute indices for bottom stack.  The bottom stack was written
	// last to the vertex buffer, so we need to offset to the index
	// of first vertex in the last ring.
	int baseIndex = (numRings-1)*numRingVertices;
	for(int i = 0; i < m_NumSlices; ++i)
	{
		m_VecIndices.push_back(southPoleIndex);
		m_VecIndices.push_back(baseIndex+i);
		m_VecIndices.push_back(baseIndex+i+1);
	}

}

void Soldier::AddTriangle (unsigned int a, unsigned int b, unsigned c)
{
	m_VecIndices.push_back(a);
	m_VecIndices.push_back(b);
	m_VecIndices.push_back(c);
}

void Soldier::BuildVertexBuffer()
{
	//fill a buffer description to copy the vertexdata into graphics memory
	D3D10_BUFFER_DESC bd;
	bd.Usage = D3D10_USAGE_DEFAULT;
	bd.ByteWidth = sizeof( VertexPosNormTex ) * m_VecVertices.size();
	bd.BindFlags = D3D10_BIND_VERTEX_BUFFER;
	bd.CPUAccessFlags = 0;
	bd.MiscFlags = 0;

	D3D10_SUBRESOURCE_DATA initData;
	initData.pSysMem = m_VecVertices.data();
	//create a ID3D10Buffer in graphics memory containing the vertex info
	HR(m_pLevel->GetDevice()->CreateBuffer( &bd, &initData, &m_pVertexBuffer ));
}
void Soldier::BuildIndexBuffer()
{
	D3D10_BUFFER_DESC ibd;
    ibd.Usage = D3D10_USAGE_IMMUTABLE;
    ibd.ByteWidth = sizeof(DWORD) * m_VecIndices.size();
    ibd.BindFlags = D3D10_BIND_INDEX_BUFFER;
    ibd.CPUAccessFlags = 0;
    ibd.MiscFlags = 0;
    D3D10_SUBRESOURCE_DATA initData;
    initData.pSysMem = m_VecIndices.data();
    HR(m_pLevel->GetDevice()->CreateBuffer(&ibd, &initData, &m_pIndexBuffer));
}

void Soldier::Draw(const RenderContext* pRenderContext)
{
	// Update Shader matrix variables
	D3DXMATRIX matView = pRenderContext->GetCamera()->GetView();
	D3DXMATRIX matProj = pRenderContext->GetCamera()->GetProj();
	m_pWorldViewProjectionVariable->SetMatrix((float*)&(m_World*matView*matProj));
	m_pWorldVariable->SetMatrix((float*)&(m_World));
	//set the texture variabele
	m_pDiffuseMapVariabele->SetResource(m_pTextureRV);	
	m_OpacityVar->SetFloat(m_Opacity);

	D3DXVECTOR3 lightDir = pRenderContext->GetLight().DirW;
	m_pLightDirVariabele->SetFloatVectorArray(&lightDir.x,0,sizeof(D3DXVECTOR3)/sizeof(float));

	// Set the input layout
    m_pLevel->GetDevice()->IASetInputLayout( m_pVertexLayout );

    // Set vertex buffer(s)
    UINT offset = 0;
	UINT vertexBufferStride = sizeof(VertexPosNormTex);
    m_pLevel->GetDevice()->IASetVertexBuffers( 0, 1, &m_pVertexBuffer, &vertexBufferStride, &offset );
   	
	// Set index buffer
	m_pLevel->GetDevice()->IASetIndexBuffer(m_pIndexBuffer,DXGI_FORMAT_R32_UINT,0);

    // Set primitive topology
    m_pLevel->GetDevice()->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST );

    D3D10_TECHNIQUE_DESC techDesc;
    m_pDefaultTechnique->GetDesc( &techDesc );
    for( UINT p = 0; p < techDesc.Passes; ++p )
    {
        m_pDefaultTechnique->GetPassByIndex(p)->Apply(0);
		m_pLevel->GetDevice()->DrawIndexed( m_VecIndices.size(), 0, 0 ); 
    }
}
void Soldier::InitActor()
{
	if(m_pActor) m_pLevel->GetPhysicsScene()->releaseActor(*m_pActor);
	
	// m_ActorDesc.body =0;//actor can not move

	m_BodyDesc.angularDamping	= 9000;
	m_BodyDesc.linearDamping	= 49000;//wrijving van het medium
	m_BodyDesc.linearVelocity = NxVec3(0,-1,0);
	m_ActorDesc.body = &m_BodyDesc;

	//determine the shape of the actor
	m_SphereShapeDesc.radius= m_Radius;
	m_ActorDesc.shapes.push_back(&m_SphereShapeDesc);

	//set the pose and density of the actor
	NxMat34 mat;
	mat.setColumnMajor44(m_World);
	m_ActorDesc.globalPose = mat;
	m_ActorDesc.density = 100.0f;
	m_pActor = m_pLevel->GetPhysicsScene()->createActor(m_ActorDesc);
	m_pActor->userData = this;//connect this object to the actor for triggering

	////m_TriggerShapeDesc.dimensions = NxVec3(1.1f ,1.1f,1.1f);
	//m_TriggerSphereDesc.radius = 20;
	////m_TriggerShapeDesc.shapeFlags |= NX_TRIGGER_ENABLE;
	//m_TriggerSphereDesc.shapeFlags |= NX_TRIGGER_ENABLE;
	//m_TriggerActorDesc.shapes.push_back(&m_TriggerSphereDesc);
	//m_TriggerActorDesc.globalPose = mat;
	//m_TriggerActor = m_pLevel->GetPhysicsScene()->createActor(m_TriggerActorDesc);
	//m_TriggerActor->userData = this;//connect this object to the actor for triggering
}

void Soldier::SetDynamic()
{
	m_bStatic = false;
	//InitActor();
}

void Soldier::AddTime(float Time)
{
	m_Time += Time;
}

float Soldier::GetTime()
{
	return m_Time;
}

void Soldier::Move()
{
	for(int i = 0; m_EnemyBulletList.size() > i; ++i)
	{
		m_EnemyBulletList.at(i)->Move();

		if(m_EnemyBulletList.at(i)->GetHit() == true)
		{
			LoseLife(2);

			if(m_Life <= 0)
			{
					m_DeleteOnlyOnce = true;
			}

			m_pLevel->RemoveChild(m_EnemyBulletList.at(i));
			vector<CubePosColorNorm*>::iterator foundElement;
			foundElement = std::find(m_EnemyBulletList.begin(),m_EnemyBulletList.end(),m_EnemyBulletList.at(i));
			if ( foundElement != m_EnemyBulletList.end())
			{
						m_EnemyBulletList.erase(foundElement);
			}		
		}
	}

	if(m_GotDestination == false && m_IsAttacking == false)
	{
		int index = 1;

		NxVec3 nextPos;
		nextPos.x = m_pathToGoal[m_pathToGoal.size() - index]->x;
		nextPos.z = m_pathToGoal[m_pathToGoal.size() - index]->z;

		D3DXVECTOR4 pos;
		D3DXVec3Transform(&pos, &D3DXVECTOR3(0, 0, 0), &m_World);

		NxVec3 Hello = NxVec3(nextPos.x, 0, nextPos.z) - NxVec3(pos.x, 0, pos.z); // position of object of pathfindinng
		Hello.normalize();
		this->Translate(D3DXVECTOR3(Hello.x/5, 0, Hello.z/5));

		D3DXVECTOR3 scale;
		D3DXQUATERNION rot;
		D3DXVECTOR3 pos2;
		D3DXMatrixDecompose(&scale, &rot, &pos2, &m_World);

		if(pos.y > 3 || pos.y < 3)
		{
			this->Translate(D3DXVECTOR3(0, 3 - pos.y, 0));
		}

		NxVec3 distance = NxVec3(nextPos.x, 0, nextPos.z) - NxVec3(pos.x, 0, pos.z);

		if(index <= m_pathToGoal.size())
		{
			float i = distance.magnitude();

			if(distance.magnitude() < 2.2f) //if radius of youre object reached a
			{
				m_pathToGoal.erase(m_pathToGoal.end() - index);
			}
		}	
	}

	if(m_pathToGoal.size() == 0 && m_GotDestination == false)
	{
		m_GotDestination = true;
	}
}

void Soldier::FindPosition()
{
	bool PosFree = false;

	NxVec3 m_Endpos;

	D3DXVECTOR4 pos;
	D3DXVec3Transform(&pos, &D3DXVECTOR3(0, 0, 0), &m_World);

	NxRay Raycast;
	Raycast.orig = NxVec3(pos.x , 20, pos.z);
	Raycast.dir = NxVec3(0, -1, 0);
	Raycast.dir.normalize();

	NxRaycastHit GetIntercect;

	NxShape *ClosestShape; 
	ClosestShape = m_pLevel->GetPhysicsScene()->raycastClosestShape(Raycast, NX_ALL_SHAPES, GetIntercect);

	if(ClosestShape)
	{
		if(GetIntercect.worldImpact.y < 0.2f) 
		{
			PosFree = true;
			m_Endpos = NxVec3(pos.x , 3, pos.z);;
		}

		else
		{
			vector<NxVec3> OpenList;
			OpenList.push_back(NxVec3(pos.x , 3, pos.z));
			while(PosFree == false)
			{

				for(int i = 0; OpenList.size() > i; ++i)
				{
					if(SeeIfAvailable(OpenList.at(i) + NxVec3(2, 0, 0)) == true  && PosFree == false) 
					{
						PosFree = true;
						m_Endpos = (OpenList.at(i) + NxVec3(2, 0, 0));
					}
					else if(PosFree == false) OpenList.push_back(OpenList.at(i) + NxVec3(2, 0, 0));

					if(SeeIfAvailable(OpenList.at(i) + NxVec3(-2, 0, 0)) == true  && PosFree == false)
					{
						PosFree = true;
						m_Endpos = OpenList.at(i) + NxVec3(-2, 0, 0);
					}
					else if(PosFree == false) OpenList.push_back(OpenList.at(i) + NxVec3(-2, 0, 0));

					if(SeeIfAvailable(OpenList.at(i) + NxVec3(0, 0, 2)) == true  && PosFree == false) 
					{
						PosFree = true;
						m_Endpos = OpenList.at(i) + NxVec3(0, 0, 2);
					}
					else if(PosFree == false) OpenList.push_back(OpenList.at(i) + NxVec3(0, 0, 2));

					if(SeeIfAvailable(OpenList.at(i) + NxVec3(0, 0, -2)) == true  && PosFree == false)
					{
						PosFree = true;
						m_Endpos = OpenList.at(i) + NxVec3(0, 0, -2);
					}
					else if(PosFree == false) OpenList.push_back(OpenList.at(i) + NxVec3(0, 0, -2));
				}
			}
			MoveToSpot(m_Endpos);
		}
	}	
}

void Soldier::MoveToSpot(NxVec3 EndPos)
{
	D3DXVECTOR3* NewEndPos = new D3DXVECTOR3(EndPos.x, 3, EndPos.z);
	m_pathToGoal.push_back(NewEndPos);
	m_GotDestination = false;
}

bool Soldier::SeeIfAvailable(NxVec3 Pos)
{
	NxRay Raycast;
	Raycast.orig = NxVec3(Pos.x , 20, Pos.z);
	Raycast.dir = NxVec3(0, -1, 0);
	Raycast.dir.normalize();

	NxRaycastHit GetIntercect;

	NxShape *ClosestShape; 
	ClosestShape = m_pLevel->GetPhysicsScene()->raycastClosestShape(Raycast, NX_ALL_SHAPES, GetIntercect);

	if(ClosestShape)
	{
		if(GetIntercect.worldImpact.y <= 0.2f) return true;
		else return false;
	}

	else return false;
}

void Soldier::OnTriggerEnter(LevelElement* element)
{
	if(element->GetNumber() == 2)
	{
		int i;
		i = 5;
	}
}

void Soldier::IsAttacking(Building* Enemy)
{
	m_IsAttacking = true;
	m_EnemyTargetBuilding = Enemy;
}

void Soldier::IsAttacking(Soldier* Enemy)
{
	m_IsAttacking = true;
	m_EnemyTargetSoldier = Enemy;
}

void Soldier::Attack(float TimePassed)
{
	m_AttackTime -= TimePassed;

	if(m_AttackTime <= 0)
	{
		D3DXVECTOR4 pos;
		D3DXVec3Transform(&pos, &D3DXVECTOR3(0, 0, 0), &m_World);

		if(m_EnemyTargetBuilding != NULL)	
		{
			if(m_EnemyTargetBuilding->GetDeleted() == true)
			{
				m_IsAttacking = false;
				m_EnemyTargetBuilding = NULL;
			}
		}

		if(m_EnemyTargetSoldier != NULL)
		{
			if(m_EnemyTargetSoldier->GetDeleted() == true)
			{
				m_IsAttacking = false;
				m_EnemyTargetSoldier = NULL;
			}
		}

		if(m_Range == 20)
		{
			if(m_EnemyTargetBuilding) m_EnemyTargetBuilding->CreateEnemyBullet(D3DXVECTOR3(pos.x, pos.y, pos.z), 10);
			if(m_EnemyTargetSoldier) m_EnemyTargetSoldier->CreateEnemyBullet(D3DXVECTOR3(pos.x, pos.y, pos.z), 10);
		}

		if(m_Range == 35)
		{
			if(m_EnemyTargetBuilding) m_EnemyTargetBuilding->CreateEnemyBullet(D3DXVECTOR3(pos.x, pos.y, pos.z), 5);
			if(m_EnemyTargetSoldier) m_EnemyTargetSoldier->CreateEnemyBullet(D3DXVECTOR3(pos.x, pos.y, pos.z), 5);
		}

		m_AttackTime = 1;
	}
}

void Soldier::RemoveSphere()
{
	for(int i = 0; m_EnemyBulletList.size() > i; ++i)
	{
		m_pLevel->RemoveChild(m_EnemyBulletList.at(i));
	}
	m_EnemyBulletList.clear();

	m_pLevel->GetPhysicsScene()->releaseActor(*m_pActor);
	m_pLevel->RemoveChild(this);
}

void Soldier::CreateEnemyBullet(D3DXVECTOR3 pos, float speed)
{
	D3DXVECTOR4 posTarget;
	D3DXVec3Transform(&posTarget, &D3DXVECTOR3(0, 0, 0), &m_World);

	CubePosColorNorm* newbullet = new CubePosColorNorm(0.5f, 0.5f, 0.5f, D3DXCOLOR(1, 1, 1, 1), D3DXVECTOR3(posTarget.x, posTarget.y, posTarget.z), speed);
	newbullet->Translate(pos);
	m_pLevel->AddChild(newbullet);
	newbullet->Initialize(m_Manager);
	m_EnemyBulletList.push_back(newbullet);
}

